package com.powernode.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.powernode.constant.BusinessEnum;
import com.powernode.domain.*;
import com.powernode.ex.handler.BusinessException;
import com.powernode.feign.OrderCartFeign;
import com.powernode.feign.OrderMemberFeign;
import com.powernode.feign.OrderProdFeign;
import com.powernode.mapper.OrderItemMapper;
import com.powernode.mapper.OrderMapper;
import com.powernode.model.OrderConfirmParam;
import com.powernode.model.Result;
import com.powernode.model.ShopOrder;
import com.powernode.service.OrderService;
import com.powernode.utils.AuthUtils;
import com.powernode.vo.OrderStatusCount;
import com.powernode.vo.OrderVo;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    @Autowired
    OrderMapper orderMapper;

    @Autowired
    OrderItemMapper orderItemMapper;

    @Autowired
    OrderMemberFeign orderMemberFeign;

    @Autowired
    OrderProdFeign orderProdFeign;

    @Autowired
    OrderCartFeign orderCartFeign;

    /**
     * 根据多个条件查询订单页面信息
     *
     * @param page        订单的分页对象，用于封装分页查询所需的信息
     * @param orderNumber 订单号，用于筛选查询结果
     * @param status      订单状态，用于筛选查询结果
     * @param startTime   开始时间，用于筛选查询结果
     * @param endTime     结束时间，用于筛选查询结果
     * @return 返回查询结果封装的分页对象
     */
    @Override
    public Page<Order> queryOrderPage(Page<Order> page, String orderNumber, Integer status, Date startTime, Date endTime) {
        // 执行分页查询，并根据条件动态构建查询Wrapper
        page = orderMapper.selectPage(page, new LambdaQueryWrapper<Order>()
                // 当状态不为空时，按状态筛选
                .eq(ObjectUtil.isNotNull(status), Order::getStatus, status)
                // 当开始时间和结束时间都不为空时，按时间范围筛选
                .between(ObjectUtil.isAllNotEmpty(startTime, endTime), Order::getCreateTime, startTime, endTime)
                // 当订单号不为空时，按订单号筛选
                .eq(StringUtils.hasText(orderNumber), Order::getOrderNumber, orderNumber)
                // 按取消时间降序排列结果
                .orderByDesc(Order::getCancelTime));
        // 获取分页查询结果中的订单记录
        List<Order> orderList = page.getRecords();
        // 检查订单列表是否为空或大小为0，避免后续处理空列表
        if (CollectionUtils.isEmpty(orderList) || orderList.size() == 0) {
            return page;
        }
        // 提取订单列表中的订单编号，用于后续可能的操作或展示
        List<String> orderNumList = orderList.stream().map(Order::getOrderNumber).collect(Collectors.toList());
        // 根据订单编号列表查询订单项列表
        // 说明：使用LambdaQueryWrapper构建查询条件，通过订单项的订单编号字段与给定的订单编号列表进行匹配
        List<OrderItem> orderItemList = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>().in(OrderItem::getOrderNumber, orderNumList));
        // 遍历订单列表，为每个订单关联其对应的订单项
        orderList.forEach(order -> {
            // 通过流过滤出与当前订单号匹配的所有订单项，并收集到一个列表中
            List<OrderItem> itemList = orderItemList.stream().filter(orderItem -> orderItem.getOrderNumber().equals(order.getOrderNumber())).collect(Collectors.toList());
            // 将收集到的订单项列表设置到当前订单中，建立订单与订单项之间的关联
            order.setOrderItems(itemList);
        });
        return page;
    }

    /**
     * 根据订单号查询订单详细信息
     *
     * @param orderNumber 订单号
     * @return 订单对象，包含订单详细信息
     */
    @Override
    public Order queryOrderDetailByOrderNumber(Long orderNumber) {
        // 根据订单号查询订单信息
        Order order = orderMapper.selectOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, orderNumber));

        // 根据订单号查询订单项信息
        List<OrderItem> orderItemList = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderNumber, orderNumber));

        // 将查询到的订单项信息设置到订单对象中
        order.setOrderItems(orderItemList);

        // 获取订单的地址ID
        Long addrOrderId = order.getAddrOrderId();
        // 调用远程接口，根据地址ID获取会员地址信息
        Result<MemberAddr> result = orderMemberFeign.getMemberAddrById(addrOrderId);
        // 如果远程接口调用失败，抛出业务异常
        if (result.getCode().equals(BusinessEnum.OPERATION_FAIL.getCode())) {
            throw new BusinessException("远程接口调用失败：根据收货地址标识查询收货地址信息");
        }
        // 获取会员地址信息，并设置到订单对象中
        MemberAddr memberAddr = result.getData();
        order.setUserAddrOrder(memberAddr);
        //远程接口调用：根据会员openid查询会员呢称
        Result<String> result1 = orderMemberFeign.getNickNameByOpenId(order.getOpenId());
        if (result1.getCode().equals(BusinessEnum.OPERATION_FAIL.getCode())) {
            throw new BusinessException("远程接口调用失败：根据会员openid查询会员呢称");
        }
        String nickName = result1.getData();
        order.setNickName(nickName);
        // 返回包含详细信息的订单对象
        return order;
    }

    /**
     * 查询会员订单状态统计
     * <p>
     * 本方法用于获取当前会员在不同状态下的订单数量统计，包括未支付、已支付和待发货的订单数
     * 它首先通过AuthUtils获取当前会员的OpenId，然后分别查询不同状态订单的数量，并将这些数量封装到OrderStatusCount对象中返回
     *
     * @return OrderStatusCount对象，包含不同状态订单的数量
     */
    @Override
    public OrderStatusCount queryMemberOrderStatusCount() {
        // 获取当前会员的OpenId
        String openId = AuthUtils.getMemberOpenId();

        // 查询用户订单待支付数量
        Long unPay = orderMapper.selectCount(new LambdaQueryWrapper<Order>().eq(Order::getOpenId, openId).eq(Order::getStatus, 1));
        // 查询用户订单待发货数量
        Long payed = orderMapper.selectCount(new LambdaQueryWrapper<Order>().eq(Order::getOpenId, openId).eq(Order::getStatus, 2));
        // 查询用户订单待收货数量
        Long consignment = orderMapper.selectCount(new LambdaQueryWrapper<Order>().eq(Order::getOpenId, openId).eq(Order::getStatus, 3));
        return OrderStatusCount.builder().unPay(Integer.valueOf(unPay.toString())).payed(Integer.valueOf(payed.toString())).consignment(Integer.valueOf(consignment.toString())).build();
    }

    /**
     * 查询会员订单分页信息
     *
     * @param current 当前页码
     * @param size    页面大小
     * @param status  订单状态（可选，0表示全部状态）
     * @return 返回订单分页对象
     */
    @Override
    public Page<Order> queryMemberOrderPage(Long current, Long size, Long status) {
        // 获取当前会员的OpenId
        String openId = AuthUtils.getMemberOpenId();
        // 创建分页对象
        Page<Order> page = new Page<>(current, size);
        // 从数据库中查询订单信息，并进行分页处理
        page = orderMapper.selectPage(page, new LambdaQueryWrapper<Order>()
                // 添加条件：openId等于传入的openId
                .eq(Order::getOpenId, openId)
                // 添加条件：订单状态不等于0时，状态等于传入的status
                .eq(0 != status, Order::getStatus, status)
                // 筛选条件：订单的删除状态等于0（表示订单未被删除）
                .eq(Order::getDeleteStatus, 0)

                // 根据创建时间降序排列结果
                .orderByDesc(Order::getCreateTime));
        // 获取分页对象中的订单记录列表
        List<Order> orderList = page.getRecords();
        // 如果订单记录列表为空或者大小为0，则直接返回分页对象
        if (CollectionUtils.isEmpty(orderList) || orderList.size() == 0) {
            return page;
        }
        // 从订单记录集合中获取订单编号集合
        List<String> orderNumberList = orderList.stream().map(Order::getOrderNumber) // 使用map函数提取每个订单的订单编号
                .collect(Collectors.toList()); // 将订单编号收集到一个新的列表中

        // 根据订单编号集合查询对应的订单项列表
        List<OrderItem> orderItemList = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>().in(OrderItem::getOrderNumber, orderNumberList));

        // 遍历订单列表，为每个订单设置对应的订单项列表
        orderList.forEach(order -> {
            // 过滤出属于当前订单的订单项
            List<OrderItem> itemList = orderItemList.stream().filter(orderItem -> orderItem.getOrderNumber().equals(order.getOrderNumber())).collect(Collectors.toList());
            // 将过滤出的订单项列表设置到当前订单中
            order.setOrderItemDtos(itemList);
        });

        // 返回查询结果
        return page;
    }

    /**
     * 根据订单编号查询会员订单详情
     *
     * @param orderNumber 订单编号，用于查询特定订单
     * @return 返回查询到的Order对象，包含订单详细信息
     * @throws BusinessException 当订单不存在或查询收货地址失败时抛出
     */
    @Override
    public Order queryMemberOrderDetailByOrderNumber(String orderNumber) {
        // 根据订单编号查询订单信息
        // 从数据库中查询订单信息
        // 使用 LambdaQueryWrapper 构建查询条件，这里是根据订单号进行查询
        Order order = orderMapper.selectOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, orderNumber));
        // Order::getOrderNumber 是使用 Java 8 的 Lambda 表达式，表示调用 Order 类的 getOrderNumber 方法
        // eq 方法用于设置查询条件，即订单号等于传入的 orderNumber 参数
        // 如果订单不存在，抛出异常提示订单编号有误
        if (ObjectUtil.isNull(order)) {
            throw new BusinessException("订单编号有误");
        }
        // 调用远程服务查询订单对应的会员收货地址信息
        Result<MemberAddr> result = orderMemberFeign.getMemberAddrById(order.getAddrOrderId());
        // 如果查询收货地址失败，抛出异常
        if (BusinessEnum.OPERATION_FAIL.getCode().equals(result.getCode())) {
            throw new BusinessException("查询订单收货地址失败");
        }
        // 获取查询到的会员收货地址信息
        MemberAddr memberAddr = result.getData();
        // 将会员收货地址信息设置到订单对象中
        order.setUserAddrDto(memberAddr);
        // 查询与订单关联的订单项信息
        // 从数据库中查询与特定订单号关联的所有订单项
        List<OrderItem> orderItems = orderItemMapper.selectList(
                // 创建一个Lambda表达式查询包装器，用于构建查询条件
                new LambdaQueryWrapper<OrderItem>()
                        // 添加查询条件，匹配订单号（orderNumber）与传入的订单号相等的记录
                        .eq(OrderItem::getOrderNumber, orderNumber));

        // 将订单项信息设置到订单对象中
        order.setOrderItemDtos(orderItems);
        // 返回包含详细信息的订单对象
        return order;
    }

    /**
     * 重写receiptMemberOrder方法，用于更新订单状态为已收货。
     *
     * @param orderNumber 订单编号
     * @return 如果更新成功，返回true；如果更新失败或未找到对应订单，返回false。
     */
    @Override
    public Boolean receiptMemberOrder(String orderNumber) {
        // 创建一个新的Order对象
        Order order = new Order();
        // 设置订单的更新时间为当前时间
        order.setUpdateTime(new Date());
        // 设置订单的最终时间为当前时间
        order.setFinallyTime(new Date());
        // 将订单状态设置为5，假设5代表已收货状态
        order.setStatus(5);
        // 使用Lambda表达式查询订单号为orderNumber的订单，并更新该订单的信息
        // 如果更新影响的行数大于0，则返回true，表示更新成功
        // 否则返回false，表示没有找到对应的订单或更新失败
        return orderMapper.update(order, new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, orderNumber)) > 0;
    }

    /**
     * 重写removeMemberOrderByOrderNumber方法，根据订单号删除订单。
     *
     * @param orderNumber 订单编号
     * @return 如果删除成功，返回true；如果删除失败或未找到对应订单，返回false。
     */
    @Override
    public Boolean removeMemberOrderByOrderNumber(String orderNumber) {
        // 创建一个新的Order对象
        Order order = new Order();
        // 设置订单的更新时间为当前时间
        order.setUpdateTime(new Date());
        // 设置订单的删除状态为1，表示已删除
        order.setDeleteStatus(1);
        // 使用Lambda表达式查询并更新订单，如果更新的记录数大于0，则返回true，表示删除成功
        return orderMapper.update(order, new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, orderNumber)) > 0;
    }

    @Override
    public OrderVo queryMemberOrderConfirmVo(OrderConfirmParam orderConfirmParam) {
        OrderVo orderVo = new OrderVo();
        String openId = AuthUtils.getMemberOpenId();
        Result<MemberAddr> result = orderMemberFeign.getMemberDefaultAddrByOpenId(openId);
        if (result.getCode().equals(BusinessEnum.OPERATION_FAIL.getCode())) {
            throw new BusinessException("远程调用：根据会员openId查询会员默认收货地址失败");
        }
        MemberAddr memberAddr = result.getData();
        orderVo.setMemberAddr(memberAddr);
        List<Long> basketIds = orderConfirmParam.getBasketIds();
        if (CollectionUtils.isEmpty(basketIds) || basketIds.size() == 0) {
            productToOrderConfirm(orderConfirmParam.getOrderItem(), orderVo);
        } else {
            cartToOrderConfirm(basketIds, orderVo);
        }
        return orderVo;
    }

    /**
     * 将购物车转换为订单确认信息
     *
     * 根据购物车的 ID 列表，生成对应的订单确认数据，包括商店订单、商品总金额、商品数量和运费等信息。
     *
     * @param basketIds 购物车 ID 列表
     * @param orderVo   用于保存订单确认信息的对象
     */
    private void cartToOrderConfirm(List<Long> basketIds, OrderVo orderVo) {
        // 检查购物车 ID 列表是否为空
        if (CollectionUtils.isEmpty(basketIds) || basketIds.size() == 0) {
            return; // 如果为空，则直接返回
        }

        // 远程调用：根据购物车 ID 列表查询购物车对象集合
        Result<List<Basket>> result = orderCartFeign.getBasketListByIds(basketIds);
        if (result.getCode().equals(BusinessEnum.OPERATION_FAIL.getCode())) {
            throw new BusinessException("远程调用：根据购物车ID集合查询购物车对象集合失败");
        }
        List<Basket> basketList = result.getData(); // 获取购物车数据

        // 提取购物车中所有 SKU ID 列表
        List<Long> skuIdList = basketList.stream().map(Basket::getSkuId).collect(Collectors.toList());

        // 远程调用：根据 SKU ID 列表查询商品 SKU 对象集合
        Result<List<Sku>> skuResult = orderProdFeign.getSkuListBySkuIds(skuIdList);
        if (skuResult.getCode().equals(BusinessEnum.OPERATION_FAIL.getCode())) {
            throw new BusinessException("远程调用：根据商品skuId集合查询商品SKU对象集合失败");
        }
        List<Sku> skuList = skuResult.getData(); // 获取 SKU 数据

        // 初始化商店订单列表
        List<ShopOrder> shopOrderList = new ArrayList<>();

        // 按商店 ID 对购物车分组
        Map<Long, List<Basket>> allShopOrderMap = basketList.stream().collect(Collectors.groupingBy(Basket::getShopId));

        // 用于存储每个商品的总金额和数量
        List<BigDecimal> oneSkuTotalAmounts = new ArrayList<>();
        List<Integer> oneSkuCounts = new ArrayList<>();

        // 遍历每个商店的购物车项
        allShopOrderMap.forEach((shopId, baskets) -> {
            ShopOrder shopOrder = new ShopOrder(); // 创建商店订单对象
            List<OrderItem> orderItemList = new ArrayList<>(); // 创建订单项列表
            shopOrder.setShopId(shopId); // 设置商店 ID

            // 遍历当前商店的购物车项
            baskets.forEach(basket -> {
                Integer prodCount = basket.getProdCount(); // 获取商品数量
                oneSkuCounts.add(prodCount); // 添加到数量列表
                Long prodId = basket.getProdId(); // 获取商品 ID
                Long skuId = basket.getSkuId(); // 获取 SKU ID

                // 创建订单项对象
                OrderItem orderItem = new OrderItem();
                orderItem.setProdCount(prodCount); // 设置商品数量
                orderItem.setShopId(shopId); // 设置商店 ID
                orderItem.setCommSts(0); // 设置商品状态，默认为 0
                orderItem.setProdId(prodId); // 设置商品 ID
                orderItem.setSkuId(skuId); // 设置 SKU ID
                orderItem.setCreateTime(new Date()); // 设置创建时间

                // 查找 SKU 对象
                Sku sku1 = skuList.stream()
                        .filter(sku -> sku.getSkuId().equals(skuId))
                        .collect(Collectors.toList()).get(0);

                // 将 SKU 信息复制到订单项对象中
                BeanUtils.copyProperties(sku1, orderItem);

                // 计算当前商品的总金额
                BigDecimal oneSkuTotalAmount = sku1.getPrice().multiply(new BigDecimal(prodCount));
                oneSkuTotalAmounts.add(oneSkuTotalAmount); // 添加到金额列表
                orderItemList.add(orderItem); // 添加到订单项列表
            });

            // 设置商店订单的订单项列表
            shopOrder.setShopOrderItems(orderItemList);
            shopOrderList.add(shopOrder); // 添加到商店订单列表
        });

        // 计算所有商品的总数量
        Integer allSkuCount = oneSkuCounts.stream().reduce(Integer::sum).get();
        orderVo.setTotalCount(allSkuCount); // 设置订单的总商品数量

        // 计算所有商品的总金额
        BigDecimal allSkuTotalAmount = oneSkuTotalAmounts.stream().reduce(BigDecimal::add).get();
        orderVo.setTotal(allSkuTotalAmount); // 设置订单的总金额
        orderVo.setActualTotal(allSkuTotalAmount); // 设置订单的实际支付金额

        // 如果总金额小于 99 元，则添加运费 6 元
        if (allSkuTotalAmount.compareTo(new BigDecimal(99)) == -1) {
            orderVo.setTransfee(new BigDecimal(6)); // 设置运费
            orderVo.setActualTotal(allSkuTotalAmount.add(new BigDecimal(6))); // 更新实际支付金额
        }

        // 设置订单的商店订单列表
        orderVo.setShopCartOrders(shopOrderList);
    }

    /**
     * 将商品项转换为购物车订单项
     * <p>
     * 根据订单项中的商品信息，将其转化为相应的购物车订单结构。
     * 如果商品存在远程获取的SKU信息，会计算商品总价并更新订单信息。
     * 最后设置总金额、商品数量以及运费等信息。
     *
     * @param orderItem 当前订单项，包含商品的SKU信息和数量等
     * @param orderVo   包含购物车信息的对象，会更新其总金额、商品项等
     */
    private void productToOrderConfirm(OrderItem orderItem, OrderVo orderVo) {
        // 获取商店ID
        Long shopId = orderItem.getShopId();

        // 初始化商店订单列表
        List<ShopOrder> shopOrderList = new ArrayList<>();
        // 创建一个新的 ShopOrder 对象
        ShopOrder shopOrder = new ShopOrder();
        // 创建一个订单项列表
        List<OrderItem> orderItemList = new ArrayList<>();

        // 获取当前订单项的 SKU ID
        Long skuId = orderItem.getSkuId();

        // 远程调用获取商品的 SKU 信息
        Result<List<Sku>> result = orderProdFeign.getSkuListBySkuIds(Arrays.asList(skuId));

        // 如果远程调用失败，抛出业务异常
        if (result.getCode().equals(BusinessEnum.OPERATION_FAIL.getCode())) {
            throw new BusinessException("远程调用：根据商品skuId查询商品sku对象 失败");
        }

        // 获取返回的 SKU 数据
        List<Sku> skuList = result.getData();
        // 假设 SKU 数据只有一个元素，获取第一个 SKU
        Sku sku = skuList.get(0);

        // 获取商品数量
        Integer prodCount = orderItem.getProdCount();
        // 计算单个商品的总金额
        BigDecimal oneSkuTotalAmount = sku.getPrice().multiply(new BigDecimal(prodCount));

        // 设置订单项的商店ID、创建时间、商品状态、商品总金额等属性
        orderItem.setShopId(shopId);
        orderItem.setCreateTime(new Date());
        orderItem.setCommSts(0); // 商品状态默认为0
        orderItem.setProductTotalAmount(oneSkuTotalAmount); // 设置商品总金额
        // 将 SKU 的信息复制到订单项中
        BeanUtils.copyProperties(sku, orderItem);

        // 将当前订单项添加到订单项列表
        orderItemList.add(orderItem);

        // 设置商店订单的信息
        shopOrder.setShopId(shopId); // 设置商店ID
        shopOrder.setShopOrderItems(orderItemList); // 设置当前商店的商品项

        // 将商店订单添加到商店订单列表
        shopOrderList.add(shopOrder);

        // 将商店订单列表赋值给订单VO对象
        orderVo.setShopCartOrders(shopOrderList);
        // 设置订单的总金额
        orderVo.setTotal(oneSkuTotalAmount);
        // 设置订单商品的数量
        orderVo.setTotalCount(prodCount);
        // 设置实际支付金额
        orderVo.setActualTotal(oneSkuTotalAmount);

        // 计算运费，如果商品总额超过99元免运费，否则运费9元
        if (oneSkuTotalAmount.compareTo(new BigDecimal(99)) == -1) {
            orderVo.setTransfee(new BigDecimal(9));
            orderVo.setActualTotal(oneSkuTotalAmount.add(new BigDecimal(9)));
        }
    }


}
